// 搭建HTTP服务的库
const http = require('http');
// 解析URL得到URL中的path部分以及'?'之后的query参数
const url = require('url');
// 文件读写库（Promise版）
const { readFile, stat} = require('fs/promises');
// 加密算法库
const crypto = require('crypto');
// 路径处理库
const { join } = require('path');

/* // 创建HTTP服务，监听3000端口
http.createServer(async (req, res) => {
  // url库的parse方法将请求中的URL拆分成路径、参数两个对象
  // 分别对应 pathname与query 这里通过解构得到 pathname
  const { pathname } = url.parse(req.url);
  // 这里filepath没有做特殊的配置, 大家可以看到在HTML文件中
  // 对于CSS、JS的文件路径我使用的是绝对路径，便是为了在此处
  // 可以做统一处理，当然也可以根据请求前缀的不同特殊配置
  // 如url: /css/index.css 可以根据文件夹前缀来配置不同的路径
  const filePath = join(__dirname, '../', pathname);
  // 这里打印输出获取的文件路径，方便后续我们观察文件缓存情况
  console.log(filePath);
  try {
    // 根据文件路径查看该文件/文件目录的状况
    const fileState = await stat(filePath);
    // 如果该路径是一个文件，则进入文件读取
    if (fileState.isFile()) {
      const file = await readFile(filePath);
      // 设置文件缓存时间为10s
      res.setHeader('Expires', new Date(Date.now() + 1000 * 10));
      // 设置HTTP1.1方式的缓存时间为10s
      res.setHeader('Cache-Control', 'max-age=10');
      res.end(file.toString());
    }
  } catch(err) {
    // 异常捕获处理
    res.statusCode = 500;
    return res.end();
  }
}).listen(3000, () => {
  console.log('server is running at port 3000.');
}); */

// 创建HTTP服务，监听3000端口
http.createServer(async (req, res) => {
  // url库的parse方法将请求中的URL拆分成路径、参数两个对象
  // 分别对应 pathname与query 这里通过解构得到 pathname
  const { pathname } = url.parse(req.url);
  // 这里filepath没有做特殊的配置, 大家可以看到在HTML文件中
  // 对于CSS、JS的文件路径我使用的是绝对路径，便是为了在此处
  // 可以做统一处理，当然也可以根据请求前缀的不同特殊配置
  // 如url: /css/index.css 可以根据文件夹前缀来配置不同的路径
  const filePath = join(__dirname, '../', pathname);
  // 这里打印输出获取的文件路径，方便后续我们观察文件缓存情况
  console.log(filePath);
  try {
    // 根据文件路径查看该文件/文件目录的状况
    const fileState = await stat(filePath);
    // 查看文件状态对象
    // console.log(fileState);
    // 如果该路径是一个文件，则进入文件读取
    if (fileState.isFile()) {
      const file = await readFile(filePath);
      // 从require对象中获取HTTP请求头中的If-None-Match
      // 注意的是nodejs中的请求头都是小写如if-none-match
      const IfNoneMatch = req.headers['if-none-match'];
      // 从require对象中获取HTTP请求头中的If-Modified-Since
      const IfModifiedSince = req.headers['if-modified-since'];
      // 获取到文件修改的时间
      // 在文件状态中有三个时间对象: ctime、mtime、atime
      // mtime对应的是上一次修改的时间
      const LastModified = fileState.mtime.toUTCString();
      // 利用md5对整个文件内容进行加密
      // 这里最好是能通过算法截取文件的某几个部分进行拼接后加密
      // 因为如果文件比较大, 整个文件内容进行加密的消耗是巨大的
      // 同时加密之后是一个对象的形式，需要转换为base64的格式
      const Etag = crypto.createHash('md5').update(file.toString()).digest('base64');
      // 优先匹配Etag是否一致，再比对时间是否相同，这里使用逻辑与
      if (Etag === IfNoneMatch && IfModifiedSince === LastModified) {
        // 如果文件没有发生修改, 返回重定向消息304
        res.statusCode = 304;
        return res.end();
      }
      // 若文件没有被浏览器缓存，或文件发送修改
      // 发送响应头Etag与LastModified
      res.setHeader('Etag', Etag);
      res.setHeader('Last-Modified', LastModified);
      res.end(file.toString());
    }
  } catch(err) {
    // 异常捕获处理
    res.statusCode = 500;
    return res.end();
  }
}).listen(3000, () => {
  console.log('server is running at port 3000.');
});

